package com 
{
	import data.*;
	
	import flash.utils.Dictionary;
	
	import graph.*;
	/**
	 * A class to represent a quest in the game, quests can be NPC
	 * buy orders, tutorial goals, and other goals.
	 * 
	 * For Buy Orders:
	 * The goal id is the same as the id of an item
	 * The amount is the quantity of that resource that is needed
	 * Progress is the amount of resources contributed
	 * 
	 * For tutorials
	 * The goal is doing some simple task, i.e. buying/selling, sailing
	 * The amount are the number of stages that must be completed (i.e. accumulate 100 gold)
	 * The progress are the stages that have been completed
	 * 
	 */
	public class GCQuest 
	{
		public static const FALSE:uint = 0;
		public static const TRUE:uint = 1;
		
		public static const DISPLAYABLE:String = "displayable";	// if set to 0, do not display, if 1 then display
		public static const XP:String = "xp";
		
		/*
		 * The unique quest identifier
		 */
		private var _qid:uint;
		
		/*
		 * The template from which to extract a description of the quest
		 */
		private var _templateId:uint;
		
		/*
		 * The id of the character who assigned the quest
		 */
		private var _assignerId:uint;
		
		/*
		 * The location where the goal needs to be completed. For some
		 * quests it can be irrelevant.
		 */
		private var _locationId:uint;
		private var _questType:uint;
		
		/*
		 * The type of goal and an amount needed to accomplish it,
		 * also keep track of the player's progress in achieving this
		 * goal.
		 * 
		 */
		private var _requestedGoalId:uint;
		private var _requestedGoalAmount:uint;
		private var _goalCurrentProgress:uint;
		
		/*
		 * The type of rewards available for completing this quest.
		 * 
		 * Rewards can be items, in which case the id is the same as the item id
		 * or it can be gold. Can be set in such a way that no rewards are given.
		 */
		private var _rewardId:uint;
		private var _rewardAmount:uint;
		 
		/*
		 * Timelimit constraints
		 */
		private var _timeRemaining:uint
		
		/*
		 * Holds any extra data about a quest
		 */
		private var _extraInfo:Dictionary;
		
		public function GCQuest(templateId:uint, assignerId:uint, lid:uint, time:uint, requestedGoalId:uint, requestedGoalAmount:uint, rewardId:uint, rewardAmount:uint) 
		{
			this._templateId = templateId;
			this._assignerId = assignerId;
			this._locationId = lid;
			this._timeRemaining = time;
			this._requestedGoalId = requestedGoalId;
			this._requestedGoalAmount = requestedGoalAmount;
			this._rewardId = rewardId;
			this._rewardAmount = rewardAmount;

			this._questType = GCQuestData.getTypeFromId(_templateId);
			this._extraInfo = new Dictionary();
			
			// By default, all quests are viewable from the ui, only for
			// certain tutorials are they hidden
			this.setQuestParameter(GCQuest.DISPLAYABLE, GCQuest.TRUE);
			
			this._goalCurrentProgress = 0;
			
		}
		
		//----------------------------------------------------------------------
		//	GET FUNCTIONS
		//----------------------------------------------------------------------
		
		public function get templateId():uint
		{
			return _templateId;
		}
		
		public function get assignerId():uint
		{
			return _assignerId;
		}
		
		public function get locationId():uint
		{
			return _locationId;
		}
		
		public function get questType():uint
		{
			return _questType;
		}
		
		public function get goalId():uint
		{
			return _requestedGoalId;
		}
		
		public function get rewardAmount():uint
		{
			return _rewardAmount;
		}
		
		public function get goalQuantity():uint
		{
			return _requestedGoalAmount;
		}
		
		public function get currentProgress():uint
		{
			return _goalCurrentProgress;
		}
		
		public function get rewardId():uint
		{
			return _rewardId;
		}
		
		public function get timeRemaining():uint
		{
			return _timeRemaining;
		}
		
		public function get qid():uint
		{
			return _qid;
		}
		
		public function set timeRemaining(time:uint):void
		{
			_timeRemaining = time;
		}
		
		public function set currentProgress(progress:uint):void
		{
			_goalCurrentProgress = progress;
		}
		
		public function set qid(id:uint):void
		{
			_qid = id;
		}
		
		public function getNPCOrderExperience():uint
		{
			// By default set the experience gained to be the same as goal quantity
			var xpModifier:uint = 1;
			if (GCPlayState.playerData.playerLevel == 3)
			{
				xpModifier = 8;
			}
			if (GCPlayState.playerData.playerLevel < 5)
			{
				xpModifier = 5;
			}
			else if (GCPlayState.playerData.playerLevel < 10)
			{
				xpModifier = 3;
			}
			else if (GCPlayState.playerData.playerLevel < 20)
			{
				xpModifier = 2;
			}
			return _requestedGoalAmount * xpModifier;
		}
		
		/**
		 * Function to return whether this quest is complete.
		 * Right now the condition is just that progess equals the
		 * requested 'goal amount'
		 * 
		 * @return
		 */
		public function isQuestComplete():Boolean
		{
			return (_goalCurrentProgress == _requestedGoalAmount);
		}
		
		/**
		 * Setting a few extra parameters, mainly to help with explaining tutorial
		 */
		public function setQuestParameter(param:String, value:uint):void
		{
			_extraInfo[param] = value;
		}
		
		 /**
		  * Getting extra parameters related to a quest
		  */
		 public function getQuestParameter(param:String):uint
		 {
			 return _extraInfo[param];
		 }
		 
		 /**
		 * A function to generate a new quest.
		 * // TODO Fix this so that not everything is so random, for different stages in the
		 * // tutorial will need to hardcode so that generated quests are fairly easy.
		 * 
		 * @return
		 */
		 public static function generateQuest():GCQuest
		{
			// Randomly pick the quest template
			var templateId:uint = Math.floor(Math.random() * 2);
			// Randomly pick the assigner id
			var assignerId:uint = Math.floor(Math.random() * 4);
			// Randomly pick the location
			//var locationId:uint = Math.floor(Math.random() * 12);
			// Randomly pick the request item
			var requestItemId:uint = Math.floor(Math.random() * 12) + 1;
			// Randomly pick the quantity of request item
			var requestQuantity:uint = Math.floor(Math.random() * GCPlayState.playerData.playerLevel ^ 2 + 100) % 30;
			// Randomly pick the reward item
			var rewardItemId:uint = 0;
			// Randomly pick the amount of reward
			var rewardQuantity:uint = requestQuantity * 40;//Math.floor(Math.random() * playerLevel ^ 3 + 1000) / 20;// % 10;
			// Randomly pick the amount of time given
			var time:uint =  Math.floor(Math.random() * 4) + 2;
			
			// Pick a city in which the player can actually travel to using their current ship
			// Use a precomputed graph to get a rough estimate of whether a player can reach a city
			var departureCityId:uint = GCPlayState.playerData.shipLocation;
			var currentShipStats:Vector.<uint> = GCPlayState.getCurrentShip().stats;
			var possibleCities:Vector.<uint> = new Vector.<uint>();
			
			// Iterate through all possible destination cities, including the one they are already at
			// and check if they can reach it using their current ship configuration.
			for (var destId:uint = 0; destId < GCCityData.NUM_CITIES; destId++)
			{
				if (GCTravelGraph.getInstance().canReachCity(departureCityId, destId, currentShipStats))
				{
					possibleCities.push(destId);
				}
			}
			
			// Randomly select an index from list of possible cities player can reach with current ship
			var cityToPickIndex:uint = Math.floor(Math.random() * possibleCities.length);
			var questLid:uint = possibleCities[cityToPickIndex];
			
			if (GCPlayState.tutorialSequence.currentTutorialStage < GCTutorial.STAGE_ESTABLISH_NEW_CENTER)
			{
				questLid = (Math.random() < 0.7) ? GCCityData.CITY_PICAROON : GCCityData.CITY_TAFFEREL;
				
				var randomGoal:uint = (Math.floor(Math.random() * 3));
				switch (randomGoal)
				{
					case 0:
						requestItemId = GCItemData.ITEM_FOOD;
						break;
					case 1:
						requestItemId = GCItemData.ITEM_LUMBER;
						break;
					case 2:
						requestItemId = GCItemData.ITEM_IRON;
						break;
				}
			}
			
			return new GCQuest(templateId, assignerId, questLid, time, requestItemId, requestQuantity, rewardItemId, rewardQuantity);
		}
		
		/**
		 * If a player is close to getting stuck, or low on gold give them a really easy quest
		 */
		public static function generateEasyQuest():GCQuest
		{
			var lid:uint = GCPlayState.getCurrentCity().getId();
			var itemToAskFor:uint = 0;
			var quantityAskFor:uint = 0;
			var goldToGive:uint = 0;
			var time:uint = 3;
			
			// Always reward a large sum of gold
			var rewardId:uint = 0;
			
			for each (var shipItem:GCItem in GCPlayState.getPlayerInventory().inventory)
			{
				if (GCItemData.getTypeFromId(shipItem.itemId) == GCItemData.ITEM_TYPE_RESOURCE)
				{
					itemToAskFor = shipItem.itemId;
					var q:uint = shipItem.quantity;
					quantityAskFor = Math.min(q, 10);
					break;
				}
			}
			
			var cityInventory:GCInventory = GCPlayState.getCurrentCity().getInventoryFromCity() ;
			if (cityInventory != null && itemToAskFor == GCItemData.ITEM_NULL)
			{
				for each (var cityItem:GCItem in cityInventory.inventory)
				{
					if (GCItemData.getTypeFromId(cityItem.itemId) == GCItemData.ITEM_TYPE_RESOURCE)
					{
						itemToAskFor = cityItem.itemId;
						quantityAskFor = Math.min(cityItem.quantity, 10);
						goldToGive = quantityAskFor * 100;
						break;
					}
				}
			}
			
			goldToGive = quantityAskFor * 100;
			if (itemToAskFor == GCItemData.ITEM_NULL)
			{
				trace("GCQuest-- player has no items in their warehouse or ship to fulfill an easy order");
				return null;
			}
			
			return new GCQuest(0, 0, lid, time, itemToAskFor, quantityAskFor, rewardId, goldToGive);
		}
	}
}